#include "common.h"
#include "utils.h"
#include <fstream>
#include <sys/ioctl.h>

using namespace std;


int socket_message;

void error(const char *str)
{
    printf("\n%s\n", str);
    exit(0);
}


int send_msg_to_address(string s, sockaddr_in address)
{
    int n = sendto(socket_message, s.c_str(), s.size() + 1, 0, (struct sockaddr*)&address, sizeof(address));
    LOG << "sent " << n << " bytes" << endl;
    return n;
}

struct sockaddr_in generate_sockaddr_int(uint32_t address, uint16_t port)
{
    sockaddr_in addr;
    memset((void *) &addr, 0, (size_t)sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = address;
    addr.sin_port = port;

    return addr;
}

int getPort(int sock)
{
    struct sockaddr_in foo;
    socklen_t len = sizeof(struct sockaddr);
    getsockname(sock, (struct sockaddr *) &foo, &len);
    return ntohs(foo.sin_port);
}


void sendFile(string filename, uint32_t address, int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    sockaddr_in server_file_upload_addr = generate_sockaddr_int(address, htons(port));
    connect(sock, (struct sockaddr*)&server_file_upload_addr, sizeof(server_file_upload_addr) );

    int remaining = getFileSize(filename);
    ifstream file(filename.c_str(), ios::in|ios::binary);
    while(1)
    {
        char buffer[128] = {0};
        int toRead = min(remaining, 128);
        file.read(buffer, toRead);
        remaining -= toRead;
        send(sock, buffer,toRead,0);
        if(remaining == 0)
            break;
    }

    close(sock);
}

bool socketContainsData(int sock)
{
    int count;
    ioctl(sock, FIONREAD, &count);
    return count > 0;
}

void sendFile(string filename, string servername, int port)
{
    sendFile(filename, inet_addr(servername.c_str()), port);
}

void receiveFile(std::string filename, int fileSize, struct sockaddr_in addr)
{

    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr_receive_file = generate_sockaddr_int(htonl(INADDR_ANY), htons(0));
    bind(listen_socket, (struct sockaddr *) &server_addr_receive_file, sizeof(server_addr_receive_file));
    int port = getPort(listen_socket);
    LOG << "bind to port " << port << endl;
    listen(listen_socket, 50);
    send_msg_to_address(commands::BeginUpload(port).serialize(), addr);
    struct sockaddr_in cli_addr;
    socklen_t len = sizeof(cli_addr);
    int sock = accept(listen_socket, (struct sockaddr *)&cli_addr, &len);
    int remaining = fileSize;


    ofstream file(filename.c_str(), ios::out | ios::binary);

    while(1)
    {
        char buffer[128] = {0};
        int n = recv(sock,buffer,127,0);
        LOG << buffer << endl;
        file.write(buffer, n);
        remaining -= n;
        if(remaining == 0 || n <= 0)
            break;
    }

    close(listen_socket);
    close(sock);

}

